package cn.baage.campus.utils;


import cn.baage.campus.config.properties.MinioProperties;
import cn.baage.campus.constant.FilePathConstant;
import cn.baage.campus.enums.ExceptionType;
import cn.baage.campus.exception.BizException;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import io.minio.RemoveObjectsArgs;
import io.minio.Result;
import io.minio.messages.DeleteError;
import io.minio.messages.DeleteObject;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @Description: Minio 工具
 * @Author: 八阿哥
 * @url: baage.cn
 * @Version: 1.0
 **/
@Component
@Slf4j
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class MinioUtils {

    private final MinioProperties minioProperties;
    private final MinioClient minioClient;


    /**
     * 上传文件
     *
     * @param file 文件
     * @return 上传结果
     */
    public String uploadFile(MultipartFile file) {
        return this.uploadFile(file, FilePathConstant.DEFAULT_PATH, true);
    }


    /**
     * 上传文件并指定目录
     *
     * @param file 文件
     * @param dir 目录
     * @return 上传结果
     */
    public String uploadFile(MultipartFile file, String dir) {
        return this.uploadFile(file, dir, true);
    }


    /**
     * 上传文件
     * @param file 上传文件
     * @param dir 文件夹
     * @return 上传结果
     */
    public String uploadFile(MultipartFile file, String dir, Boolean dataClassify) {
        // 判断文件是否为空
        if (file == null || file.getSize() == 0) {
            throw new BizException(ExceptionType.FILE_EMPTY_SIZE_ERROR);
        }
        // 文件的原始名称
        String originalFileName = file.getOriginalFilename();
        // 文件的 Content-Type
        String contentType = file.getContentType();
        // 生成存储对象的名称（将 UUID 字符串中的 - 替换成空字符串）
        String key =IdUtil.simpleUUID();
        // 获取文件的后缀，如 .jpg
        String suffix = Optional.ofNullable(originalFileName).map(n -> n.substring(n.lastIndexOf("."))).orElse(null);
        if (StrUtil.isBlank(suffix)) {
            throw new BizException(ExceptionType.FILE_NAME_ERROR);
        }
        // 拼接上文件后缀，即为要存储的文件名
        String objectName = String.format("%s%s", key, suffix);
        String path;
        if (dataClassify) {
            LocalDate now = LocalDate.now();
            DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd");
            String dataPath = now.format(dtf);
            path = String.format("%s/%s/%s", dir, dataPath, objectName);
        } else {
            path = String.format("%s/%s", dir, objectName);
        }
        log.info("开始上传文件至 Minio, 路径: {}", path);
        // 上传文件至 Minio
        try {
            minioClient.putObject(PutObjectArgs.builder()
                    .bucket(minioProperties.getBucketName())
                    .object(path)
                    .stream(file.getInputStream(), file.getSize(), -1)
                    .contentType(contentType)
                    .build());
        } catch (Exception e) {
            log.error("上传文件至 Minio 失败，错误信息：{}", e.getMessage());
            throw new BizException(ExceptionType.FILE_UPLOAD_ERROR);
        }
        log.info("上传文件至 Minio 成功，路径: {}", path);
        return path;
    }


    /**
     * 删除文件
     *
     * @param filenames 文件名称列表
     */
    public void delFile(List<String> filenames) {
        List<DeleteObject> deleteObjects = filenames.stream().map(DeleteObject::new).collect(Collectors.toList());
        RemoveObjectsArgs build = RemoveObjectsArgs.builder()
                .bucket(minioProperties.getBucketName())
                .objects(deleteObjects).build();
        minioClient.removeObjects(build);
    }


    /**
     * 获取访问URL
     *
     * @param path 相对路径
     * @return  可访问URL
     */
    public String getAccessUrl(String path) {
        return String.format("%s/%s", minioProperties.getBaseAccessUrl(), path);
    }

}